package com.school.service.impl;

import com.school.common.ResultCode;
import com.school.entity.User;
import com.school.entity.dto.LoginRequest;
import com.school.entity.dto.LoginResponse;
import com.school.exception.BusinessException;
import com.school.mapper.UserMapper;
import com.school.service.AuthService;
import com.school.util.JwtUtil;
import com.school.util.PasswordUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

/**
 * 认证服务实现类
 * 
 * @author School Development Team
 * @version 1.0.0
 * @since 2024
 */
@Service
@Transactional
public class AuthServiceImpl implements AuthService {

    private static final Logger logger = LoggerFactory.getLogger(AuthServiceImpl.class);

    private static final String TOKEN_BLACKLIST_PREFIX = "auth:blacklist:";
    private static final String LOGIN_ATTEMPT_PREFIX = "auth:attempt:";
    private static final int MAX_LOGIN_ATTEMPTS = 5;
    private static final int LOCKOUT_DURATION_MINUTES = 30;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public LoginResponse login(LoginRequest loginRequest) {
        String username = loginRequest.getUsername();
        String password = loginRequest.getPassword();

        logger.info("用户登录尝试: {}", username);

        // 检查登录尝试次数
        checkLoginAttempts(username);

        // 查询用户
        User user = userMapper.findByUsernameDebug(username);
        logger.info("查询用户结果: {}", user);
        if (user == null) {
            recordFailedLoginAttempt(username);
            throw new BusinessException(ResultCode.LOGIN_FAILED, "用户名或密码错误");
        }

        // 检查用户状态
        if ("locked".equals(user.getStatus())) {
            throw new BusinessException(ResultCode.ACCOUNT_LOCKED, "账户已被锁定，请联系管理员");
        }
        if ("inactive".equals(user.getStatus())) {
            throw new BusinessException(ResultCode.ACCOUNT_DISABLED, "账户已被禁用，请联系管理员");
        }

        // 验证密码
        if (!PasswordUtil.matches(password, user.getPasswordHash())) {
            recordFailedLoginAttempt(username);
            throw new BusinessException(ResultCode.PASSWORD_ERROR, "用户名或密码错误");
        }

        // 清除失败登录记录
        clearFailedLoginAttempts(username);

        // 更新登录信息
        updateLoginInfo(user);

        // 生成JWT令牌
        String accessToken = jwtUtil.generateAccessToken(user.getUserId(), user.getUsername(), user.getUserType());
        String refreshToken = jwtUtil.generateRefreshToken(user.getUserId(), user.getUsername());

        // 构建用户信息
        LoginResponse.UserInfo userInfo = new LoginResponse.UserInfo(
                user.getUserId(),
                user.getUsername(),
                user.getUserType(),
                user.getRelatedId(),
                user.getEmail(),
                user.getPhone(),
                user.getStatus(),
                user.getLastLoginTime(),
                user.getLoginCount()
        );

        // 构建登录响应
        LoginResponse loginResponse = new LoginResponse(
                accessToken,
                refreshToken,
                jwtUtil.getExpiration(),
                userInfo
        );

        logger.info("用户登录成功: {}, 用户类型: {}", username, user.getUserType());
        return loginResponse;
    }

    @Override
    public boolean logout(String token) {
        try {
            // 验证令牌
            if (!jwtUtil.validateAccessToken(token)) {
                return false;
            }

            String userId = jwtUtil.getUserIdFromToken(token);
            String username = jwtUtil.getUsernameFromToken(token);
            
            // 将令牌加入黑名单
            String blacklistKey = TOKEN_BLACKLIST_PREFIX + token;
            long expiration = jwtUtil.getExpirationDateFromToken(token).getTime() - System.currentTimeMillis();
            if (expiration > 0) {
                redisTemplate.opsForValue().set(blacklistKey, "blacklisted", expiration, TimeUnit.MILLISECONDS);
            }

            logger.info("用户登出成功: {}", username);
            return true;
        } catch (Exception e) {
            logger.error("用户登出失败: {}", e.getMessage());
            return false;
        }
    }

    @Override
    public LoginResponse refreshToken(String refreshToken) {
        try {
            // 验证刷新令牌
            if (!jwtUtil.validateRefreshToken(refreshToken)) {
                throw new BusinessException(ResultCode.TOKEN_INVALID, "刷新令牌无效");
            }

            String userId = jwtUtil.getUserIdFromToken(refreshToken);
            String username = jwtUtil.getUsernameFromToken(refreshToken);

            // 查询用户
            User user = userMapper.selectById(userId);
            if (user == null || !"active".equals(user.getStatus())) {
                throw new BusinessException(ResultCode.TOKEN_INVALID, "用户状态异常");
            }

            // 生成新的访问令牌
            String newAccessToken = jwtUtil.generateAccessToken(user.getUserId(), user.getUsername(), user.getUserType());
            String newRefreshToken = jwtUtil.generateRefreshToken(user.getUserId(), user.getUsername());

            // 构建用户信息
            LoginResponse.UserInfo userInfo = new LoginResponse.UserInfo(
                    user.getUserId(),
                    user.getUsername(),
                    user.getUserType(),
                    user.getRelatedId(),
                    user.getEmail(),
                    user.getPhone(),
                    user.getStatus(),
                    user.getLastLoginTime(),
                    user.getLoginCount()
            );

            logger.info("令牌刷新成功: {}", username);
            return new LoginResponse(newAccessToken, newRefreshToken, jwtUtil.getExpiration(), userInfo);
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            logger.error("令牌刷新失败: {}", e.getMessage());
            throw new BusinessException(ResultCode.TOKEN_INVALID, "令牌刷新失败");
        }
    }

    @Override
    public boolean validateToken(String token) {
        try {
            // 检查令牌是否在黑名单中
            String blacklistKey = TOKEN_BLACKLIST_PREFIX + token;
            if (Boolean.TRUE.equals(redisTemplate.hasKey(blacklistKey))) {
                return false;
            }

            // 验证令牌
            return jwtUtil.validateAccessToken(token);
        } catch (Exception e) {
            logger.error("令牌验证失败: {}", e.getMessage());
            return false;
        }
    }

    @Override
    public boolean changePassword(String userId, String oldPassword, String newPassword) {
        try {
            // 查询用户
            User user = userMapper.selectById(userId);
            if (user == null) {
                throw new BusinessException(ResultCode.DATA_NOT_FOUND, "用户不存在");
            }

            // 验证旧密码
            if (!PasswordUtil.matches(oldPassword, user.getPasswordHash())) {
                throw new BusinessException(ResultCode.PASSWORD_ERROR, "原密码错误");
            }

            // 验证新密码格式
            PasswordUtil.PasswordValidationResult validationResult = PasswordUtil.validatePassword(newPassword);
            if (!validationResult.isValid()) {
                throw new BusinessException(ResultCode.PARAM_INVALID, validationResult.getMessage());
            }

            // 加密新密码
            String newPasswordHash = PasswordUtil.encodePassword(newPassword);

            // 更新密码
            int result = userMapper.updatePassword(userId, newPasswordHash);
            if (result > 0) {
                logger.info("用户密码修改成功: {}", user.getUsername());
                return true;
            }
            return false;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            logger.error("密码修改失败: {}", e.getMessage());
            throw new BusinessException(ResultCode.SYSTEM_ERROR, "密码修改失败");
        }
    }

    @Override
    public boolean resetPassword(String username, String newPassword) {
        try {
            // 查询用户
            User user = userMapper.findByUsername(username);
            if (user == null) {
                throw new BusinessException(ResultCode.DATA_NOT_FOUND, "用户不存在");
            }

            // 验证新密码格式
            PasswordUtil.PasswordValidationResult validationResult = PasswordUtil.validatePassword(newPassword);
            if (!validationResult.isValid()) {
                throw new BusinessException(ResultCode.PARAM_INVALID, validationResult.getMessage());
            }

            // 加密新密码
            String newPasswordHash = PasswordUtil.encodePassword(newPassword);

            // 更新密码
            int result = userMapper.updatePassword(user.getUserId(), newPasswordHash);
            if (result > 0) {
                logger.info("用户密码重置成功: {}", username);
                return true;
            }
            return false;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            logger.error("密码重置失败: {}", e.getMessage());
            throw new BusinessException(ResultCode.SYSTEM_ERROR, "密码重置失败");
        }
    }

    @Override
    public boolean lockUser(String userId) {
        try {
            int result = userMapper.lockUser(userId);
            if (result > 0) {
                logger.info("用户锁定成功: {}", userId);
                return true;
            }
            return false;
        } catch (Exception e) {
            logger.error("用户锁定失败: {}", e.getMessage());
            return false;
        }
    }

    @Override
    public boolean unlockUser(String userId) {
        try {
            int result = userMapper.unlockUser(userId);
            if (result > 0) {
                logger.info("用户解锁成功: {}", userId);
                return true;
            }
            return false;
        } catch (Exception e) {
            logger.error("用户解锁失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 检查登录尝试次数
     */
    private void checkLoginAttempts(String username) {
        String attemptKey = LOGIN_ATTEMPT_PREFIX + username;
        Integer attempts = (Integer) redisTemplate.opsForValue().get(attemptKey);
        if (attempts != null && attempts >= MAX_LOGIN_ATTEMPTS) {
            throw new BusinessException(ResultCode.ACCOUNT_LOCKED, 
                    String.format("登录失败次数过多，账户已被临时锁定%d分钟", LOCKOUT_DURATION_MINUTES));
        }
    }

    /**
     * 记录失败登录尝试
     */
    private void recordFailedLoginAttempt(String username) {
        String attemptKey = LOGIN_ATTEMPT_PREFIX + username;
        Integer attempts = (Integer) redisTemplate.opsForValue().get(attemptKey);
        attempts = (attempts == null) ? 1 : attempts + 1;
        redisTemplate.opsForValue().set(attemptKey, attempts, LOCKOUT_DURATION_MINUTES, TimeUnit.MINUTES);
        
        logger.warn("用户登录失败: {}, 失败次数: {}", username, attempts);
    }

    /**
     * 清除失败登录记录
     */
    private void clearFailedLoginAttempts(String username) {
        String attemptKey = LOGIN_ATTEMPT_PREFIX + username;
        redisTemplate.delete(attemptKey);
    }

    /**
     * 更新登录信息
     */
    private void updateLoginInfo(User user) {
        LocalDateTime now = LocalDateTime.now();
        Integer loginCount = (user.getLoginCount() == null) ? 1 : user.getLoginCount() + 1;
        userMapper.updateLoginInfo(user.getUserId(), now, loginCount);
        
        // 更新内存中的用户信息
        user.setLastLoginTime(now);
        user.setLoginCount(loginCount);
    }
}